<?php

require_once 'IEntityHandler.php';

class DbEntityManager implements IEntityHandler
{
    /**
     *
     * @var AnnotationsParser
     */
    protected $annotationsParser;

    /**
     *
     * @var ICacheHandler
     */
    protected $cacheHandler;

    /**
     *
     * @var IDbHandler
     */
    protected $dbHandler;

    /**
     *
     * @param IDbHandler $dbHandler
     * @param ICacheHandler $cacheHandler
     * @return <type>
     */
    public function __construct(IDbHandler $dbHandler, ICacheHandler $cacheHandler = null)
    {
        $this->dbHandler = $dbHandler;
        $this->cacheHandler = $cacheHandler;
        $this->annotationsParser = new AnnotationsParser();
        return $this;
    }

    /**
     *
     * @param <type> $entityName
     * @return entityName
     */
    public function create($entityName)
    {
        return new $entityName();
    }

    /**
     *
     * @param <type> $entity
     */
    public function delete($entity)
    {
        if ($entity === null) {
            throw new Exception('Entity can not be null.');
        }

        $primaryKeyColumn = $this->getPrimaryKeyColumn($entity);
        $table = $this->getEntityTable($entity);
        
        $sql = 'DELETE FROM ' . $table . ' WHERE ' . $primaryKeyColumn . ' = ?';
        $primaryKeyValue = $entity->$primaryKeyColumn;
        return $this->dbHandler->query($sql, array($primaryKeyValue));
    }

    /**
     *
     * @param <type> $entityName
     * @param <type> $id
     * @return <type>
     */
    public function get($entityName, $id)
    {
        $table = $this->getEntityTable($entityName);
        $primaryKeyColumn = $this->getPrimaryKeyColumn($entityName);
        $sql = 'SELECT * FROM ' . $table . ' WHERE ' . $primaryKeyColumn . ' = ?';
        $result = $this->dbHandler->query($sql, array($id));
        if (!count($result)) {
            return null;
        }
        $entity = $this->create($entityName);
        $entityColums = $this->getEntityColumns($entityName);
        $row = $result->getFirstRow();
        foreach ($entityColums as $entityColumn) {
            $entity->$entityColumn = $row->$entityColumn;
        }
        return $entity;
    }

    /**
     *
     * @param <type> $entityName
     * @param <type> $extras
     * @return <type>
     */
    public function getCollection($entityName, $extras = null)
    {
        $table = $this->getEntityTable($entityName);
        $primaryKeyColumn = $this->getPrimaryKeyColumn($entityName);
        
        $sql = 'SELECT * FROM ' . $table . ' ' . $extras;
        $result = $this->dbHandler->query($sql);
        if (!count($result)) {
            return null;
        }

        $entities = array();
        $entityColums = $this->getEntityColumns($entityName);
        foreach ($result as $row) {
            $entity = new $entityName();
            foreach ($entityColums as $entityColumn) {
                $entity->$entityColumn = $row->$entityColumn;
            }
            $entities[$entity->$primaryKeyColumn] = $entity;
        }
        return $entities;
    }

    /**
     *
     * @param <type> $entity
     * @return <type>
     */
    protected function getEntityColumns($entity)
    {
        $propertiesAnnotations = $this->annotationsParser
                                      ->getPropertyAnnotations($entity);
        $columns = array();
        foreach ($propertiesAnnotations as $propertyName => $propertyAnnotations) {
            if (isset($propertyAnnotations['Type'])) {
                $columns[] = $propertyName;
            }
        }
        return $columns;
    }

    /**
     *
     * @param <type> $entity
     * @return <type>
     */
    protected function getEntityValues($entity)
    {
        $propertiesAnnotations = $this->annotationsParser
                                      ->getPropertyAnnotations($entity);
        $columns = array();
        foreach ($propertiesAnnotations as $propertyName => $propertyAnnotations) {
            if (isset($propertyAnnotations['Type'])) {
                $columns[$propertyName] = $entity->$propertyName;
            }
        }
        return $columns;
    }

    /**
     *
     * @param <type> $entity
     * @return <type>
     */
    protected function getPrimaryKeyColumn($entity)
    {
        $propertiesAnnotations = $this->annotationsParser
                                      ->getPropertyAnnotations($entity);
        foreach ($propertiesAnnotations as $propertyName => $propertyAnnotations) {
            if (isset($propertyAnnotations['PrimaryKey'])) {
                return $propertyName;
            }
        }
        return null;
    }

    /**
     *
     * @param <type> $entity
     * @return <type>
     */
    protected function doSave($entity)
    {
        $primaryKeyColumn = $this->getPrimaryKeyColumn($entity);

        $table = $this->getEntityTable($entity);

        $columnsAndValues = $this->getEntityValues($entity);

        $columns = array_keys($columnsAndValues);
        $values = array_values($columnsAndValues);
        if (empty($entity->$primaryKeyColumn)) {
            $sql = 'INSERT INTO ' . $table . ' (' . implode(', ', $columns)
                 . ') VALUES (' . trim(str_repeat('?, ', count($values)), ', ') . ')';
            
            $result = $this->dbHandler->query($sql, $values);
            $lastInsertId = $result->getLastInsertId();
            if ($lastInsertId === null) {
                throw new Exception('Could not insert entity.');
            }
            $entity->$primaryKeyColumn = $lastInsertId;

        } else {
            $columnsAndValuesSql = '';
            foreach ($columns as $column) {
                $columnsAndValuesSql .= $column . ' = ?, ';
            }
            $sql = 'UPDATE ' . $table . ' SET '
                 . trim($columnsAndValuesSql, ' ,') . ' WHERE '
                 . $primaryKeyColumn . ' = ?';
            $values[] = $entity->$primaryKeyColumn;
            $result = $this->dbHandler->query($sql, $values);
            if ($result->getAffectedRows() == 0) {
                throw new Exception('Could not update entity.');
            }
		}
        return $entity;
    }

    /**
     *
     * @param <type> $entity
     * @return <type>
     */
    protected function getEntityTable($entity)
    {
        $classAnnotations = $this->annotationsParser
                                 ->getClassAnnotations($entity);
        if (!isset($classAnnotations['Table'])) {
            throw new Exception('Unknown entity table.');
        }
        return $classAnnotations['Table'];
    }


    /**
     *
     * @param <type> $entity
     * @return <type>
     */
    public function save($entity)
    {
        if ($entity === null) {
            throw new Exception('Entity can not be null.');
        }

        try {
            return $this->doSave($entity);
        } catch (Exception $e) {
            $this->createTable($entity);
            return $this->doSave($entity);
        }

//        $sql = 'SELECT * FROM information_schema.tables WHERE table_schema = "maikuro" AND table_name = "' . $classAnnotations['Table'] . '"';
//        $sql = 'SHOW TABLES like "' . $classAnnotations['Table'] . '"';
        // $this->dbHandler->tableExists($tableName)
//        $result = $this->dbHandler->query($sql);
//        if (!count($result)) {
//            $this->createTable($classAnnotations, $propertiesAnnotations);
//        }

    }

    /**
     *
     * @param <type> $classAnnotations
     * @param <type> $propertyAnnotations 
     */
    protected function createTable($classAnnotations, $propertyAnnotations)
    {
        $propertyAnnotations = $this->annotationsParser
                                    ->getPropertyAnnotations($entity);
        $table = $this->getEntityTable($entity);
        $this->dbHandler->createTable($table, $propertyAnnotations, true);
    }

}